package algo3c3g3.model.action;

import algo3c3g3.model.*;
import algo3c3g3.model.material.*;

/**
 * Esta accion hace que la unidad se mueva sobre bloques solidos.
 * La unidad se convierte en Faller si no hay un piso que la sostenga.
 */
public class Walker extends Action
{
    private static final int UPHILL_RANGE = 1;
    private static final int DOWNHILL_RANGE = 1;
    
    public void perform(Unit unit)
    {
        unit.materialEffectY();
        
        // No hay piso.
        if(unit.overSolidFloor() == false)
        {
            walkDownOrFall(unit);
            return;
        }
        
        // Hay piso. Camina.
        
        if(unit.nextBlockIsSolid())
        {
            walkStraightOrUp(unit);
        }
        else
        {
            unit.materialEffectX();
            unit.walk();
        }
    }
    
    // Esto es solo para mostrar cuando se cambia de accion.
    public Walker()
    {
        //System.out.println("\nAccion cambiada a Walker\n");
    }
    
    protected void walkDownOrFall(Unit unit)
    {
        // Verifica si puede caminar hacia abajo.
        if(checkDownHill(unit))
        {
            unit.fall();
            unit.walk();
        }
        // Si no se convierte en Faller.
        else
        {
            unit.setAction(new Faller());
            unit.fall(); // Esto es para que no quede flotando en el aire antes de empezar a caer.
        }
    }
    
    private void walkStraightOrUp(Unit unit)
    {
        // Verifica si puede caminar hacia arriba
        if(checkUpHill(unit))
        {
            unit.moveUp();
            unit.walk();
        }
        // Si no se mueve normalmente cambiando de direccion
        // si es necesario.
        else
        {
            unit.materialEffectX();

            if(unit.nextBlockIsSolid() == false) // Esto es para que no quede
                                                 // dentro de un bloque solido.
                unit.walk();
        }
    }
    
    /*
     * Verifica si hay algun bloque solido debajo unidad 
     * en el rango indicado por DOWNHILL_RANGE. Si hay devuelve true.
     */
    private boolean checkDownHill(Unit unit)
    {
        for(int i = 1; i <= DOWNHILL_RANGE + 1; i++)
        {
            if(unit.getOwnerMap().blockAt(unit.getXPosition(), unit.getYPosition() + i).isSolid())
                return true;
        }
        
        return false;
    }
    
    /*
     * Verifica si hay algun bloque no solido encima de la unidad y en 
     * la posicion siguiente sobre x en el rango indicado por UPHILL_RANGE.
     * Si hay devuelve true.
     */
    private boolean checkUpHill(Unit unit)
    {
        if(unit.getOwnerMap().blockAt(unit.getNextSidePosition()).getMaterial().toString().equals(Material.INVISIBLE_BLOCK))
            return false;
        
        int xpos = unit.getXPosition() + unit.getDirection();
        int ypos = unit.getYPosition();
        
        for(int i = 1; i <= UPHILL_RANGE; i++)
        {
            if(unit.getOwnerMap().blockAt(xpos, ypos - i).isSolid() == false)
                return true;
        }
        
        return false;
    }
    
    
    
    public static void main(String[] args)
    {
        /* Esta prueba representa una unidad moviendose en linea recta
         * en un mapa que tiene una fila de bloques de aire.
         * Se puede ver como la unidad cambia de direccion al chocar con un bloque de tierra */
        
        System.out.println();
        
        World world = new World(new Coord(3, 1), null, new TestMaps.TestUpDownHillMap());
        
        System.out.println("Mapa:");
        world.printMap();
        
        world.addUnit();
        
        Unit unit1 = world.getUnit(0);
        
        System.out.println("\nNueva unidad:");
        unit1.printStatus();
        
        System.out.println("\nCaminando:");
        for(int i = 0; i < 20; i++)
        {
            world.moveUnits();
            unit1.printStatus();
        }
        
        System.out.println("\nNueva unidad:");
        world.addUnit();
        Unit unit2 = world.getUnit(1);
        unit2.setPosition(13, 8);
        
        System.out.println("\nCaminando:");
        for(int i = 0; i < 20; i++)
        {
            world.moveUnits();
            unit2.printStatus();
        }
    }
}
